/*
 * cpu.cpp
 *
 *  Created on: Feb 1, 2012
 *      Author: dgdiniz
 */

#include <iostream>
#include <stdio.h>
#include "cpu.hpp"

using namespace std;


void cpu::reset(){
	z80_reset();

    for (int i=0; i<MEMORY_SIZE; i++) Memory[i]=0;

	programSize = 0;
	runningProgram = false;
    hasProgramLoaded = false;
}


void cpu::reset_program(){
    z80_reset();
    runningProgram = false;
}


int cpu::doDebug(const std::string& Msg)
{
    QString str = QString::fromUtf8( Msg.data(), Msg.size() );
    _txtEdit->insertPlainText(str);

    return 0;
}


u8 cpu::read8(u16 address, int cycles){
#ifdef Z80_TEST
	OpTCycles += cycles;
	OpMCycles++;

    if (address >= MEMORY_SIZE) return 0;
#endif
	return Memory[address];
}

void cpu::write8(u16 address, u8 data, int cycles){
#ifdef Z80_TEST
	OpTCycles += cycles;
	OpMCycles++;

    if (address >= MEMORY_SIZE) return;
#endif

	Memory[address] = data;
}

void cpu::sync(int cycles)
{
#ifdef Z80_TEST
	OpTCycles += cycles;
	OpMCycles++;
#endif
}

void cpu::retCallback(int cycles){
	if (regs.pc >= programSize)
	{
        runningProgram = false;
        Debug("\n\nProgram Finished - Number of intruction executed: %d.\n", inst_count);
        Debug("Reseting program.\n\n");

        reset_program();
	}
}


u32 cpu::load_program(const char *programPath){

    FILE * programFile;
    size_t numBytesReadFromFile;
    u32 result = 0;

    reset(); //Reseting CPU before loading the new program

    programFile = fopen(programPath, "rb");

    if (programFile)
    {
        // obtain file size:
        fseek (programFile , 0 , SEEK_END);
        programSize = ftell (programFile);
        rewind (programFile);

        if (programSize >= MEMORY_SIZE) programSize = MEMORY_SIZE;

        // copy the file into the buffer:
        numBytesReadFromFile = fread (Memory,1,programSize,programFile);

        if (numBytesReadFromFile != programSize)
        {
            Debug("Error loading program file to Memory!!!\n");
            hasProgramLoaded = false;
            result = 1;
        }
        else
        {
            hasProgramLoaded = true;
        }

        fclose (programFile); //We don't need this anymore hehe
    }
    else
    {
        Debug("Cannot open the program file. Check the file!\n");
        hasProgramLoaded = false;
        result = 2;
    }

    return result;
}


u32 cpu::run_loaded_program(){

    if (hasProgramLoaded == false)
    {
        Debug("There is no program loaded. Load a program first!\n");
        return 1;
    }

    runningProgram = true; //starting the program. We need to set this
    while ( (runningProgram) && (regs.pc < MEMORY_SIZE) && (inst_count < MAX_OPCODES_PER_PROGRAM))
    {
        z80_step();
    }

    return 0;
}


void cpu::cpu_step(){

    if (hasProgramLoaded == false)
    {
        Debug("There is no program loaded. Load a program first!\n");
        return;
    }

    if (regs.pc < MEMORY_SIZE)
    {
        z80_step();
    }
    else
    {
        Debug("Trying to run a instruction on a invalid area!\n");
    }
}


u32 cpu::load_and_run_program(const char *programPath){
    u32 result;

    result = load_program(programPath);

    if (result != 0) return result;

    result = run_loaded_program();

    return result;
}


u8 cpu::getReg(u32 reg){
    return *(regs.r[reg]);
}

void cpu::setReg(u32 reg, u8 valueToSet){
    *(regs.r[reg]) = valueToSet;
}

u16 cpu::getRegPair(u32 reg){
    return *(regs.pair[reg]);
}

void cpu::setRegPair(u32 reg, u16 valueToSet){
    *(regs.pair[reg]) = valueToSet;
}

u16 cpu::getPC(){
    return regs.pc;
}

void cpu::setPC(u16 pcValueToSet){
    regs.pc = pcValueToSet;
}

u16 cpu::getSP(){
    return regs.sp;
}
u16 cpu::setSP(u16 pcValueToSet){
    regs.sp = pcValueToSet;
}

void cpu::memoryDump(u16 start, u16 size)
{
    if ((start+size) > MEMORY_SIZE)
	{
        Debug("We cannot dump this region of Memory!\n");
		return;
	}

	for (int i=0; i<size; i+=8) //TODO: fix this thing. For a full memory this will fail.
    {
        Debug("%X %X %X %X   %X %X %X %X\n",  (int) Memory[start+i], (int) Memory[start+i+1], (int) Memory[start+2], (int) Memory[start+i+3],
              (int) Memory[start+i+4], (int) Memory[start+i+5], (int) Memory[start+i+6], (int) Memory[start+i+7]);

    }
}

void cpu::registersDump()
{
    Debug("\n\nRegisters Dump: \n\n");

    Debug("A: %X  ---  ", (int) regs.A);
    Debug("B: %X  ---  ", (int) regs.B);
    Debug("C: %X  ---  ", (int) regs.C);
    Debug("D: %X  ---  ", (int) regs.D);
    Debug("E: %X  ---  ", (int) regs.E);
    Debug("H: %X  ---  ", (int) regs.H);
    Debug("L: %X \n", (int) regs.L);

    Debug("AF: %X  ---  ", (int) regs.AF);
    Debug("BC: %X  ---  ", (int) regs.BC);
    Debug("DE: %X  ---  ", (int) regs.DE);
    Debug("HL: %X \n", (int) regs.HL);

    Debug("PC: %X  ---  ", (int) regs.pc);
    Debug("SP: %X \n\n", (int) regs.sp);
}



void cpu::test_processor(){

    load_and_run_program("/home/dgdiniz/Downloads/z80asm/ADD/add1");
	registersDump();
}

void cpu::test_memory(){

	load_program("/home/dgdiniz/Downloads/z80asm/a.bin");
	memoryDump(0, 32);
}


void cpu::test_registers(){

    *regs.r[0] = 1;

    Debug("\n\nTesting Basic Functions. Testing r[]  - ");
    if (regs.BC == 0x100 && regs.B == 0x1 && regs.C == 0x0)
    {
        Debug("OK\n");
    }
    else
    {
        Debug("ERROR\n");
    }

    z80_reset();

    regs.BC = 2;

    Debug("Testing Basic Functions. Testing pair - ");
    if (regs.BC == 0x2 && regs.B == 0x0 && regs.C == 0x2)
    {
        Debug("OK\n");
    }
    else
    {
        Debug("ERROR\n");
    }


    regs.Flags.h = 1;

    Debug("Testing Flags                         - ");
    if (regs.F == 0x10 && regs.Flags.h == 0x1)
    {
        Debug("OK\n");
    }
    else
    {
        Debug("ERROR\n");
    }
}

